package optimizer;

import java.util.ArrayList;

public class DependencyTableRow {
	private int cycleNumber;
	private int orininalLineNumber;

	private Instruction instruction;
	private Register writtenTo;
	private Register readFrom1;
	private Register readFrom2;

	private ArrayList<Register> dependencies;

	DependencyTableRow(int orininalLineNumber,
			Instruction ins, Tracker t, Boolean isDelayedBranchSlotInstruction) {

		boolean isDependent = false;
		
		String previousInstruction = t.getIns().op;
		// branch instruction match <also appear in DependencyTable:locateDelayedBranchSlot()>
		if(!isDelayedBranchSlotInstruction) {
			if ("BNEZ".equalsIgnoreCase(previousInstruction) || "BEQZ".equalsIgnoreCase(previousInstruction)) {
				isDependent = true;
			}
		}
		if (t.isRegisterLock(ins.readFrom1) || t.isRegisterLock(ins.readFrom2)) {
			isDependent = true;
		}
		
		// if dependency test fails
		if(isDependent) {
			// Add stalls
			this.cycleNumber = t.getClockcycle();
			this.orininalLineNumber = -1;

			this.instruction = new Instruction (null, "STALL", null, null, null, -1, null,"<STALL>");
			this.dependencies = t.getDependencies();
			

			t.runClockPulse(this.instruction);
		}
		
		// if dependency check positive
		else {
			this.cycleNumber = t.getClockcycle();
			this.orininalLineNumber = orininalLineNumber;

			this.instruction = ins;
			this.writtenTo = ins.writtenTo;
			this.readFrom1 = ins.readFrom1;
			this.readFrom2 = ins.readFrom2;

			this.dependencies = t.getDependencies();


			t.runClockPulse(this.instruction);
			t.setRegisterLock(this.writtenTo, getOperationLengthToWrite(this.instruction.op));
		}
	}
	
	
	int getOperationLengthToWrite(String operation) {
		int length = 1;
		// "LW", "SW", 
		// "LI", "ADD", "SUB"
		// "LT", "EQ", "GT", 
		// "BEQZ", "GOTO" 
		// "MULT", "DIV", "PWR"
		// "DATA", "HALT", "IN", "OUT"
		
		if("LW".equalsIgnoreCase(operation)) length = 1;
		else if("SW".equalsIgnoreCase(operation)) length = 1;
		
		else if("LI".equalsIgnoreCase(operation)) length = 1;
		else if("ADD".equalsIgnoreCase(operation)) length = 1;
		else if("SUB".equalsIgnoreCase(operation)) length = 1;
		
		else if("LT".equalsIgnoreCase(operation)) length = 1;
		else if("EQ".equalsIgnoreCase(operation)) length = 1;
		else if("GT".equalsIgnoreCase(operation)) length = 1;
		
		else if("MULT".equalsIgnoreCase(operation)) length = 3;
		else if("DIV".equalsIgnoreCase(operation)) length = 5;
		else if("PWR".equalsIgnoreCase(operation)) length = 10;
		
		else if("BEQZ".equalsIgnoreCase(operation)) length = 1;
		else if("GOTO".equalsIgnoreCase(operation)) length = 1;
		
		else if("DATA".equalsIgnoreCase(operation)) length = 1;
		else if("HALT".equalsIgnoreCase(operation)) length = 1;
		else if("IN".equalsIgnoreCase(operation)) length = 1;
		else if("OUT".equalsIgnoreCase(operation)) length = 1;
		
		else { 
			System.out.println("DependencyTableRow.gegetOperationLengthToWritet() : Unknown operation - '"+ operation+"'");
		}
		return length;
	}		

	void printDependencyRowHeading() {
		System.out.println("______________________________________________________________");
		System.out.println("|Cycle#\t|Line#\t|W Reg\t|R Regs\t\t|Op\t|Dependencies");
		System.out.println("|-------|-------|-------|---------------|-------|-------------");
		
	}

	public static String getHeader() {
		String header = "";
		header = header.concat("______________________________________________________________\n");
		header = header.concat("|Cycle#\t|Line#\t|W Reg\t|R Regs\t\t|Op\t|Dependencies\n");
		header = header.concat("|-------|-------|-------|---------------|-------|-------------\n");
		return header;
	}

	public String getLine() {
		String line = "";
		line = line.concat("|" + this.cycleNumber + "\t|");
		line = line.concat(this.orininalLineNumber /*+ " ("+ (this.instruction.label!=null?this.instruction.label:"-") +")" */+ "\t|");

		if(this.writtenTo!= null) 
			line = line.concat(this.writtenTo + "\t|");
		else
			line = line.concat("  \t|");


		if(this.readFrom1!= null && this.readFrom2!= null) 
			line = line.concat(this.readFrom1 + ", " + this.readFrom2 + "    \t|");
		else if(this.readFrom1== null && this.readFrom2== null)
			line = line.concat("         \t|");
		else if(this.readFrom1 != null){
			line = line.concat(this.readFrom1 + "       \t|");
		}
		else {
			System.out.print(this.readFrom2 + "       \t|");
		}
		line = line.concat(this.instruction.op + "\t|");
		
		for(int i=0; i<this.dependencies.size(); i++) {
			if(i>0) line = line.concat(", ");
			line = line.concat(this.dependencies.get(i).toString());
		}
		line = line.concat("\n");
		return line;
	}
	
	public static String getFooter() {
		String footer = "";
		footer = footer.concat("|=======|=======|=======|===============|=======|=============");
		return footer;
	}

	void printDependencyRow() {
		System.out.print("|" + this.cycleNumber + "\t|");
		System.out.print(this.orininalLineNumber /*+ " ("+ (this.instruction.label!=null?this.instruction.label:"-") +")" */+ "\t|");

		if(this.writtenTo!= null) 
			System.out.print(this.writtenTo + "\t|");
		else
			System.out.print("  \t|");


		if(this.readFrom1!= null && this.readFrom2!= null) 
			System.out.print(this.readFrom1 + ", " + this.readFrom2 + "    \t|");
		else if(this.readFrom1== null && this.readFrom2== null)
			System.out.print("         \t|");
		else if(this.readFrom1 != null){
			System.out.print(this.readFrom1 + "       \t|");
		}
		else {
			System.out.print(this.readFrom2 + "       \t|");
		}
		System.out.print(this.instruction.op + "\t|");
		
		for(int i=0; i<this.dependencies.size(); i++) {
			if(i>0) System.out.print(", ");
			System.out.print(this.dependencies.get(i));
		}
		System.out.println();

	}

	int getCycleNumber() {
		return cycleNumber;
	}

	void setCycleNumber(int cycleNumber) {
		this.cycleNumber = cycleNumber;
	}

	int getOrininalLineNumber() {
		return orininalLineNumber;
	}

	void setOrininalLineNumber(int orininalLineNumber) {
		this.orininalLineNumber = orininalLineNumber;
	}

	Instruction getInstruction() {
		return instruction;
	}


	Register getWrittenTo() {
		return writtenTo;
	}

	void setWrittenTo(Register writtenTo) {
		this.writtenTo = writtenTo;
	}

	Register getReadFrom1() {
		return readFrom1;
	}

	void setReadFrom1(Register readFrom1) {
		this.readFrom1 = readFrom1;
	}

	Register getReadFrom2() {
		return readFrom2;
	}

	void setReadFrom2(Register readFrom2) {
		this.readFrom2 = readFrom2;
	}

	ArrayList<Register> getDependencies() {
		return dependencies;
	}

	void setDependencies(ArrayList<Register> dependencies) {
		this.dependencies = dependencies;
	}
}
